import numpy as np
import scipy.special
import matplotlib.pyplot as plt


class NeuralNetwork:
    def __init__(self, input_nodes: int, hidden_nodes: int, output_nodes: int, learning_rate: float):
        """
        初始化神经网络
        :param input_nodes: 输入层节点个数
        :param hidden_nodes: 隐藏层节点个数
        :param output_nodes: 输出层节点个数
        :param learning_rate: 学习率
        """
        self.input_nodes = input_nodes
        self.hidden_nodes = hidden_nodes
        self.output_nodes = output_nodes
        self.learning_rate = learning_rate

        self.w_input_hidden = np.random.normal(0.0, pow(self.hidden_nodes, -0.5),
                                               (self.hidden_nodes, self.input_nodes))
        self.w_hidden_output = np.random.normal(0.0, pow(self.output_nodes, -0.5),
                                                (self.output_nodes, self.hidden_nodes))

        self.activation_function = lambda x: scipy.special.expit(x)

    def train(self, input_list: list, target_list: list):
        """
        训练函数
        :param input_list: 输入层节点值对应的矩阵
        :param target_list: 理论输出矩阵
        :return:
        """
        inputs = np.array(input_list, ndmin=2).T
        targets = np.array(target_list, ndmin=2).T

        hidden_inputs = np.dot(self.w_input_hidden, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)

        final_inputs = np.dot(self.w_hidden_output, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)

        output_errors = targets - final_outputs
        hidden_errors = np.dot(self.w_hidden_output.T, output_errors)

        self.w_hidden_output += self.learning_rate * np.dot(output_errors * final_outputs * (1.0 - final_outputs),
                                                            hidden_outputs.T)

        self.w_input_hidden += self.learning_rate * np.dot(hidden_errors * hidden_outputs * (1.0 - hidden_outputs),
                                                           np.transpose(inputs))
        return final_outputs

    def query(self, input_list: list):
        """
        使用训练好的神经网络老获取相对应的结果
        :param input_list: 输入矩阵
        :return: 神经网络计算好的结果
        """
        inputs = np.array(input_list, ndmin=2).T

        hidden_inputs = np.dot(self.w_input_hidden, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)

        final_inputs = np.dot(self.w_hidden_output, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)

        return final_outputs


# 初始化变量
input_nodes = 784
hidden_nodes = 100
output_nodes = 10
learning_rate = 0.1

# 生成神经网络
n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

# 读取训练集
train_data_file = open("mnist_train.csv", 'r')
train_data_list = train_data_file.readlines()
train_data_file.close()

# 开始训练
epochs = 5
for e in range(epochs):
    for record in train_data_list:
        all_values = record.split(',')
        inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
        targets = np.zeros(output_nodes) + 0.01
        targets[int(all_values[0])] = 0.99
        outputs = n.train(inputs, targets)
        # print('目标值:', all_values[0], '训练值', np.argmax(outputs))

# 读取测试集
test_data_file = open("mnist_test.csv", 'r')
test_data_list = test_data_file.readlines()
test_data_file.close()

# 开始测试
scorecard = []
for record in test_data_list:
    all_values = record.split(',')
    correct_label = int(all_values[0])
    inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
    outputs = n.query(inputs)
    label = np.argmax(outputs)
    if label == correct_label:
        scorecard.append(1)
    else:
        scorecard.append(0)

# 输出测试结果
scorecard_array = np.asarray(scorecard)
print('最终性能:', scorecard_array.sum() / scorecard_array.size)

"""for i in range(10):
    image_array = np.asfarray(test_data_list[i].split(',')[1:]).reshape(28, 28)
    plt.imshow(image_array, cmap="Greys", interpolation="None")
    plt.show()
    print(test_data_list[i][0], end="\t")
"""
